/* Copyright (C) 2010 Luca Piccinelli
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "pipeline/pipeline_io.h"

namespace NAMESPACE{

// ****** GenericIOElement implementation **************************************

//  _____________________________________________________
// |   Standard constructors                             |
// |_____________________________________________________|

GenericIOElement::~GenericIOElement(){
    delete io_key;
}
GenericIOElement::GenericIOElement() : IOElement(), element(0){
    io_key = new IOKey();
}
GenericIOElement::GenericIOElement(const GenericIOElement& gie) : IOElement(gie),
                                                                  element(gie.element),
                                                                  io_key( new IOKey(*(gie.io_key)) ){}
GenericIOElement& GenericIOElement::operator=(const GenericIOElement& gie){
    if(&gie == this) return *this;

    IOKey* tmp_key = io_key;
    element_t* const tmp_el = element;

    element = 0;
    io_key = 0;

    try{
        io_key = new IOKey(*(gie.io_key));
        element = gie.element;
    }catch(...){
        if(io_key) delete io_key;
        io_key = tmp_key;
        element = tmp_el;

        throw;
    }

    delete tmp_key;
    return *this;
}
// ......................................................


//  _____________________________________________________
// |   Other constructors                                |
// |_____________________________________________________|

GenericIOElement::GenericIOElement(const IOKey& k) : IOElement(), element(0){
    io_key = new IOKey(k);
}

// ......................................................

boost::any& GenericIOElement::getElement() const throw(bad_element){
    if(!element) throw bad_element("no held element");

    return *element;
}
void GenericIOElement::setElement(boost::any* const el){
    element = el;
}

const IOKey& GenericIOElement::getIOKey() const {
    return *io_key;
}

IOElement& GenericIOElement::setIOKey(const IOKey& k){
    *io_key = k;
    return *this;
}
IOElement& GenericIOElement::setIOKey(const char* _ns, const char* _key_name){
    std::string ns_str = _ns;
    std::string key_name_str = _key_name;
    this->setIOKey(ns_str, key_name_str);
    return *this;
}
IOElement& GenericIOElement::setIOKey(const std::string& _ns, std::string& _key_name){
    io_key->set_key(_ns, _key_name);
    return *this;
}
IOElement& GenericIOElement::setIOKey(const std::string& _ns, std::string& _key_name, const std::string& type){
    io_key->set_key(_ns, _key_name);
    io_key->setType(type);
    return *this;
}

bool GenericIOElement::operator==(const IOElement& _element) const{
    const GenericIOElement* gie = dynamic_cast<const GenericIOElement*>(&_element);
    if(!gie) return false;

    return *io_key == *(gie->io_key) &&
           element == gie->element;
}

bool GenericIOElement::operator!=(const IOElement& _element) const{
    return !GenericIOElement::operator==(_element);
}
// -----------------------------------------------------------------------------

// ****** HandledIOElement implementation **************************************

//  _____________________________________________________
// |   Standard constructors                             |
// |_____________________________________________________|

HandledIOElement::~HandledIOElement(){
    delete element;
}
HandledIOElement::HandledIOElement() : GenericIOElement(){}

HandledIOElement::HandledIOElement(const HandledIOElement& hie) : GenericIOElement(hie){
    element = new element_t(*(hie.element));
}

HandledIOElement& HandledIOElement::operator=(const HandledIOElement& hie){
    if(&hie == this) return *this;

    element_t* tmp_any = element;

    GenericIOElement::operator =(hie);
    try{
        element = new element_t(*(hie.element));
    }catch(...){
        element = tmp_any;
        throw;
    }
    if(tmp_any)delete tmp_any;

    return *this;
}
// ......................................................

void HandledIOElement::setElement(boost::any* const el){
    if(!element) element = new element_t();
    *element = *el;
}

//  _____________________________________________________
// |   Other constructors                                |
// |_____________________________________________________|

HandledIOElement::HandledIOElement(const IOKey& k) : GenericIOElement(k){}

template<typename T>
HandledIOElement::HandledIOElement(const T& _element) : GenericIOElement(){
    element = new element_t(_element);
}
// ......................................................

bool HandledIOElement::operator==(const IOElement& _element) const{
    const HandledIOElement* hie = dynamic_cast<const HandledIOElement*>(&_element);
    if(!hie) return false;

    return *io_key == *(hie->io_key);
}

bool HandledIOElement::operator!=(const IOElement& _element) const{
    return !HandledIOElement::operator==(_element);
}

// -----------------------------------------------------------------------------

// ****** NumericIOElement implementation **************************************

//  _____________________________________________________
// |   Standard constructors                             |
// |_____________________________________________________|

NumericIOElement::~NumericIOElement(){}
NumericIOElement::NumericIOElement() : HandledIOElement(){}
NumericIOElement::NumericIOElement(const NumericIOElement& nie) : HandledIOElement(){
    HandledIOElement::setElement(nie.element);
}
NumericIOElement& NumericIOElement::operator=(const NumericIOElement& nie){
    if(&nie == this) return *this;
    HandledIOElement::operator=(nie);
    return *this;
}
// ......................................................

void NumericIOElement::setElement(boost::any* const el) throw(bad_element){
    if(el->type() != typeid(int)    &&
       el->type() != typeid(double) &&
       el->type() != typeid(float)) throw bad_element("NaN");

    HandledIOElement::setElement(el);
}

//  _____________________________________________________
// |   Other constructors                                |
// |_____________________________________________________|

NumericIOElement::NumericIOElement(int n) : HandledIOElement(n){}
NumericIOElement::NumericIOElement(double n) : HandledIOElement(n){}
NumericIOElement::NumericIOElement(float n) : HandledIOElement(n){}

NumericIOElement::NumericIOElement(int n, const IOKey& k) : HandledIOElement(n, k){}
NumericIOElement::NumericIOElement(double n, const IOKey& k) : HandledIOElement(n, k){}
NumericIOElement::NumericIOElement(float n, const IOKey& k) : HandledIOElement(n, k){}

// ......................................................

bool NumericIOElement::operator==(const IOElement& _element) const{
    const NumericIOElement* nie = dynamic_cast<const NumericIOElement*>(&_element);
    if(!nie) return false;

    double n1 = 0;
    if(element->type() == typeid(double))
            n1 = boost::any_cast<double>(*element);
    else if(element->type() == typeid(double))
            n1 = boost::any_cast<float>(*element);
    else
            n1 = boost::any_cast<int>(*element);

    double n2 = 0;
    if(nie->element->type() == typeid(double))
            n2 = boost::any_cast<double>(*(nie->element));
    else if(nie->element->type() == typeid(double))
            n2 = boost::any_cast<float>(*(nie->element));
    else
            n2 = boost::any_cast<int>(*(nie->element));

    return *io_key == *(nie->io_key) &&
           n1 == n2;
}

bool NumericIOElement::operator!=(const IOElement& _element) const{
    return !NumericIOElement::operator==(_element);
}
// -----------------------------------------------------------------------------

}